import playwrightApi from '../lib/playwright.js'
import { sleep } from '../util/util.js'

class DouBao {
  constructor() {
    this.page = null
    this.timeout = 5 * 60 * 1000 // 5分钟
  }

  initRoute(koa) {
    console.log('[豆包] 登录', 'GET http://127.0.0.1:3001/login')
    console.log('[豆包] 对话', 'POST http://127.0.0.1:3001/message')
    console.log('[豆包] 测试', 'GET http://127.0.0.1:3001/test')

    koa.router.get('/', ctx => {
      this.start()

      ctx.body = {
        code: 0,
        msg: 'success',
      }
    })

    koa.router.get('/login', ctx => {
      this.chat()

      ctx.body = {
        code: 0,
        msg: 'success',
      }
    })

    koa.router.get('/test', async ctx => {
      try {
        const res = await this.message('你好')

        ctx.body = {
          code: 0,
          msg: 'success',
          data: res,
        }
      } catch (e) {
        console.error(e)

        ctx.body = {
          code: 1,
          msg: e?.message || 'success',
        }
      }
    })

    koa.router.post('/message', async ctx => {
      try {
        const { body } = ctx.request

        if (!body.message) throw new Error('缺少消息')
        if (body.timeout != undefined) this.timeout = body.timeout

        const res = await this.message(body.message)

        ctx.body = {
          code: 0,
          msg: 'success',
          data: res,
        }
      } catch (e) {
        console.error(e)

        ctx.body = {
          code: 1,
          msg: e?.message || 'success',
        }
      }
    })
  }

  log(...args) {
    console.log('[豆包]', ...args)
  }

  // 开始聊天
  // async chat() {
  //   const { context } = playwrightApi

  //   if (!this.page) this.page = await context.newPage()

  //   this.log('打开豆包')
  //   await this.page.goto('https://www.doubao.com/chat/')
  // }

  // 开始聊天
  async chat() {
    const { context } = playwrightApi

    if (this.page) {
      console.log('关闭标签，重新打开')
      await this.page.close()
      this.page = null
    }

    this.page = await context.newPage()
    await this.page.goto('https://www.doubao.com/chat/')
    this.log('打开豆包')
  }

  // 发送消息
  async sendMessage(message) {
    this.log('发送消息')

    const { page } = this
    await page.getByTestId('chat_input').locator('textarea').fill(message)

    // 等待文件上传
    await page.waitForFunction(
      () => {
        const button = document.querySelector('#flow-end-msg-send')
        return button?.getAttribute('aria-disabled') === 'false'
      },
      undefined,
      { timeout: this.timeout }
    )

    await sleep(3000)

    // 点击发送
    await page.locator('#flow-end-msg-send').click()
  }

  // 判断是否启用深度思考
  async think() {
    const { page } = this

    const normal = page.locator('[data-testid="deep_think_select_button"]')
    const noNormal = page.locator('[data-testid="use-deep-thinking-switch-btn"]')

    // 正常思考
    if ((await normal.count()) > 0) {
      this.log('正常深度思考')
      const thinkBtnChecked = await normal.getAttribute('data-checked')
      return thinkBtnChecked === 'true' ? true : false
    }
    // 被禁用的情况
    else if ((await noNormal.count()) > 0) {
      this.log('被禁用的深度思考')
      const thinkBtn = await page.locator('[data-testid="use-deep-thinking-switch-btn"] button')
      const thinkBtnChecked = await thinkBtn.getAttribute('data-checked')
      return thinkBtnChecked === 'true' ? true : false
    } else {
      return false
    }
  }

  // 等待结果
  async getResult() {
    this.log('等待结果')

    const { page } = this
    const result = {}

    // 等待跳转回答页面
    // await page.waitForSelector('[data-testid="immersive_side_bar_chat"]', { timeout: this.timeout })
    await page.waitForSelector('[data-testid="message_text_content"]', { timeout: this.timeout })
    this.log('跳转成功，等待回答')

    // 等待回答完成
    // await page.waitForSelector('[data-testid="chat_input_local_break_button"]', {
    //   state: 'hidden',
    //   timeout: this.timeout,
    // })
    await page.waitForSelector('[data-testid="audio_play_button"]', { timeout: this.timeout })
    this.log('回答完成')

    result.think = await this.think()

    // 代码模式
    // data-testid="canvas_panel_container"

    if (result.think) {
      this.log('获取深度思考')

      const el = await page.$('[data-testid="collapse_quota_block"]')

      // 先判断是否存在 collapse_quota_block
      // if ((await collapseLocator.count()) > 0) {
      if (el) {
        // 获取最后一个
        const collapseLocator = page.locator('[data-testid="collapse_quota_block"]')
        const lastCollapse = collapseLocator.last()

        // 第一个 message_text_content
        const firstContent = lastCollapse.locator('[data-testid="message_text_content"]').first()

        result.thinkText = await firstContent.textContent()
        result.thinkHtml = await firstContent.innerHTML()
      } else {
        this.log('没有深度思考内容')
      }
    }

    // 文本消息
    this.log('获取正文')
    result.contentText = await page.locator('[data-testid="message_text_content"]').last().textContent()
    result.contentHtml = await page.locator('[data-testid="message_text_content"]').last().innerHTML()

    console.log('思考结束', result)

    return result
  }

  // 文本对话
  async message(message) {
    // options = {
    //   // think: true,  // 深度思考（后续实现自动切换）
    // }

    await this.chat()
    await this.sendMessage(message)
    return await this.getResult()
  }

  // 生成贝塞尔曲线路径
  generateBezierPath(start, end, steps = 30) {
    const points = []
    // 随机两个控制点，制造曲线
    const cp1 = {
      x: start.x + (end.x - start.x) * Math.random(),
      y: start.y + (end.y - start.y) * 0.3 * Math.random(),
    }
    const cp2 = {
      x: start.x + (end.x - start.x) * Math.random(),
      y: start.y + (end.y - start.y) * 0.3 + (end.y - start.y) * Math.random() * 0.3,
    }

    for (let t = 0; t <= 1; t += 1 / steps) {
      const x =
        Math.pow(1 - t, 3) * start.x +
        3 * Math.pow(1 - t, 2) * t * cp1.x +
        3 * (1 - t) * Math.pow(t, 2) * cp2.x +
        Math.pow(t, 3) * end.x
      const y =
        Math.pow(1 - t, 3) * start.y +
        3 * Math.pow(1 - t, 2) * t * cp1.y +
        3 * (1 - t) * Math.pow(t, 2) * cp2.y +
        Math.pow(t, 3) * end.y
      points.push({ x, y })
    }
    return points
  }
}

export default new DouBao()
